/*!
 * \file WtBtPorter.cpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 *
 * \brief
 */
#include "WtBtPorter.h"
#include "WtBtRunner.h"

#include "../WtBtCore/CtaMocker.h"
#include "../WtBtCore/WtHelper.h"

#include "../WTSTools/WTSLogger.h"

#include "../Includes/WTSVersion.h"

#ifdef _WIN32
#ifdef _WIN64
char PLATFORM_NAME[] = "X64";
#else
char PLATFORM_NAME[] = "X86";
#endif
#else
char PLATFORM_NAME[] = "UNIX";
#endif

WtBtRunner& getRunner() {
    static WtBtRunner runner;
    return runner;
}

void register_evt_callback(FuncEventCallback cbEvt) {
    getRunner().registerEvtCallback(cbEvt);
}

void register_cta_callbacks(FuncStraInitCallback cbInit,
                            FuncStraTickCallback cbTick,
                            FuncStraCalcCallback cbCalc,
                            FuncStraBarCallback cbBar,
                            FuncSessionEvtCallback cbSessEvt,
                            FuncStraCalcCallback cbCalcDone /* = NULL*/,
                            FuncStraCondTriggerCallback cbCondTrigger) {
    getRunner().registerCtaCallbacks(cbInit, cbTick, cbCalc, cbBar, cbSessEvt,
                                     cbCalcDone, cbCondTrigger);
}

void register_sel_callbacks(FuncStraInitCallback cbInit,
                            FuncStraTickCallback cbTick,
                            FuncStraCalcCallback cbCalc,
                            FuncStraBarCallback cbBar,
                            FuncSessionEvtCallback cbSessEvt,
                            FuncStraCalcCallback cbCalcDone /* = NULL*/) {
    getRunner().registerSelCallbacks(cbInit, cbTick, cbCalc, cbBar, cbSessEvt,
                                     cbCalcDone);
}

void register_hft_callbacks(
    FuncStraInitCallback cbInit, FuncStraTickCallback cbTick,
    FuncStraBarCallback cbBar, FuncHftChannelCallback cbChnl,
    FuncHftOrdCallback cbOrd, FuncHftTrdCallback cbTrd,
    FuncHftEntrustCallback cbEntrust, FuncStraOrdDtlCallback cbOrdDtl,
    FuncStraOrdQueCallback cbOrdQue, FuncStraTransCallback cbTrans,
    FuncSessionEvtCallback cbSessEvt) {
    getRunner().registerHftCallbacks(cbInit, cbTick, cbBar, cbChnl, cbOrd,
                                     cbTrd, cbEntrust, cbOrdDtl, cbOrdQue,
                                     cbTrans, cbSessEvt);
}

void register_ext_data_loader(FuncLoadFnlBars fnlBarLoader,
                              FuncLoadRawBars rawBarLoader,
                              FuncLoadAdjFactors fctLoader,
                              FuncLoadRawTicks tickLoader, bool bAutoTrans) {
    getRunner().registerExtDataLoader(fnlBarLoader, rawBarLoader, fctLoader,
                                      tickLoader, bAutoTrans);
}

void feed_raw_bars(WTSBarStruct* bars, WtUInt32 count) {
    getRunner().feedRawBars(bars, count);
}

void feed_raw_ticks(WTSTickStruct* ticks, WtUInt32 count) {
    getRunner().feedRawTicks(ticks, count);
}

void init_backtest(const char* logProfile, bool isFile, const char* outDir) {
    static bool inited = false;

    if (inited)
        return;

    getRunner().init(logProfile, isFile, outDir);

    inited = true;
}

void config_backtest(const char* cfgfile, bool isFile) {
    static bool inited = false;

    if (inited)
        return;

    if (strlen(cfgfile) == 0)
        getRunner().config("configbt.json", true);
    else
        getRunner().config(cfgfile, isFile);
}

void set_time_range(WtUInt64 stime, WtUInt64 etime) {
    getRunner().set_time_range(stime, etime);
}

void enable_tick(bool bEnabled /* = true */) {
    getRunner().enable_tick(bEnabled);
}

void run_backtest(bool bNeedDump, bool bAsync) {
    getRunner().run(bNeedDump, bAsync);
}

void stop_backtest() { getRunner().stop(); }

void release_backtest() { getRunner().release(); }

WtString get_raw_stdcode(const char* stdCode) {
    return getRunner().get_raw_stdcode(stdCode);
}

const char* get_version() {
    static std::string _ver;
    if (_ver.empty()) {
        _ver = PLATFORM_NAME;
        _ver += " ";
        _ver += WT_VERSION;
        _ver += " Build@";
        _ver += __DATE__;
        _ver += " ";
        _ver += __TIME__;
    }
    return _ver.c_str();
}

void clear_cache() { getRunner().clear_cache(); }

void write_log(WtUInt32 level, const char* message, const char* catName) {
    if (strlen(catName) > 0) {
        WTSLogger::log_raw_by_cat(catName, (WTSLogLevel)level, message);
    }
    else {
        WTSLogger::log_raw((WTSLogLevel)level, message);
    }
}

CtxHandler init_cta_mocker(const char* name, int slippage /* = 0*/,
                           bool hook /* = false*/, bool persistData /* = true*/,
                           bool bIncremental /* = false*/,
                           bool bRatioSlp /* = false*/) {
    return getRunner().initCtaMocker(name, slippage, hook, persistData,
                                     bIncremental, bRatioSlp);
}

#pragma region "CTA策略接口"
void cta_enter_long(CtxHandler cHandle, const char* stdCode, double qty,
                    const char* userTag, double limitprice, double stopprice) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_enter_long(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_exit_long(CtxHandler cHandle, const char* stdCode, double qty,
                   const char* userTag, double limitprice, double stopprice) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_exit_long(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_enter_short(CtxHandler cHandle, const char* stdCode, double qty,
                     const char* userTag, double limitprice, double stopprice) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_enter_short(stdCode, qty, userTag, limitprice, stopprice);
}

void cta_exit_short(CtxHandler cHandle, const char* stdCode, double qty,
                    const char* userTag, double limitprice, double stopprice) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_exit_short(stdCode, qty, userTag, limitprice, stopprice);
}

WtUInt32 cta_get_bars(CtxHandler cHandle, const char* stdCode,
                      const char* period, WtUInt32 barCnt, bool isMain,
                      FuncGetBarsCallback cb) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;
    try {
        WTSKlineSlice* kData =
            ctx->stra_get_bars(stdCode, period, barCnt, isMain);
        if (kData) {
            WtUInt32 reaCnt = (WtUInt32)kData->size();

            for (uint32_t i = 0; i < kData->get_block_counts(); i++)
                cb(cHandle, stdCode, period, kData->get_block_addr(i),
                   kData->get_block_size(i),
                   i == kData->get_block_counts() - 1);

            kData->release();
            return reaCnt;
        }
        else {
            return 0;
        }
    } catch (...) {
        return 0;
    }
}

WtUInt32 cta_get_ticks(CtxHandler cHandle, const char* stdCode,
                       WtUInt32 tickCnt, FuncGetTicksCallback cb) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;
    try {
        WTSTickSlice* tData = ctx->stra_get_ticks(stdCode, tickCnt);
        if (tData) {
            uint32_t thisCnt = min(tickCnt, (WtUInt32)tData->size());
            if (thisCnt != 0)
                cb(cHandle, stdCode, (WTSTickStruct*)tData->at(0), thisCnt,
                   true);
            else
                cb(cHandle, stdCode, NULL, 0, true);

            tData->release();
            return thisCnt;
        }
        else {
            return 0;
        }
    } catch (...) {
        return 0;
    }
}

double cta_get_position_profit(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position_profit(stdCode);
}

WtUInt64 cta_get_detail_entertime(CtxHandler cHandle, const char* stdCode,
                                  const char* openTag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_entertime(stdCode, openTag);
}

double cta_get_detail_cost(CtxHandler cHandle, const char* stdCode,
                           const char* openTag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_cost(stdCode, openTag);
}

double cta_get_detail_profit(CtxHandler cHandle, const char* stdCode,
                             const char* openTag, int flag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_detail_profit(stdCode, openTag, flag);
}

double cta_get_position_avgpx(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position_avgpx(stdCode);
}

void cta_get_all_position(CtxHandler cHandle, FuncGetPositionCallback cb) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL) {
        cb(cHandle, "", 0, true);
        return;
    }

    ctx->enum_position(
        [cb, cHandle](const char* stdCode, double qty) {
            cb(cHandle, stdCode, qty, false);
        },
        false);

    cb(cHandle, "", 0, true);
}

double cta_get_position(CtxHandler cHandle, const char* stdCode,
                        bool bOnlyValid, const char* openTag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_position(stdCode, bOnlyValid, openTag);
}

double cta_get_fund_data(CtxHandler cHandle, int flag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_fund_data(flag);
}

void cta_set_position(CtxHandler cHandle, const char* stdCode, double qty,
                      const char* userTag, double limitprice,
                      double stopprice) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_set_position(stdCode, qty, userTag, limitprice, stopprice);
}

WtUInt64 cta_get_first_entertime(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_first_entertime(stdCode);
}

WtUInt64 cta_get_last_entertime(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_entertime(stdCode);
}

WtUInt64 cta_get_last_exittime(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_exittime(stdCode);
}

double cta_get_last_enterprice(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_enterprice(stdCode);
}

WtString cta_get_last_entertag(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return 0;

    return ctx->stra_get_last_entertag(stdCode);
}

double cta_get_price(const char* stdCode) {
    return getRunner().replayer().get_cur_price(stdCode);
}

double cta_get_day_price(const char* stdCode, int flag) {
    return getRunner().replayer().get_day_price(stdCode, flag);
}

WtUInt32 cta_get_tdate() { return getRunner().replayer().get_trading_date(); }

WtUInt32 cta_get_date() { return getRunner().replayer().get_date(); }

WtUInt32 cta_get_time() { return getRunner().replayer().get_min_time(); }

void cta_log_text(CtxHandler cHandle, WtUInt32 level, const char* message) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    switch (level) {
    case LOG_LEVEL_DEBUG:
        ctx->stra_log_debug(message);
        break;
    case LOG_LEVEL_INFO:
        ctx->stra_log_info(message);
        break;
    case LOG_LEVEL_WARN:
        ctx->stra_log_warn(message);
        break;
    case LOG_LEVEL_ERROR:
        ctx->stra_log_error(message);
        break;
    default:
        break;
    }
}

void cta_save_userdata(CtxHandler cHandle, const char* key, const char* val) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_save_user_data(key, val);
}

WtString cta_load_userdata(CtxHandler cHandle, const char* key,
                           const char* defVal) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return defVal;

    return ctx->stra_load_user_data(key, defVal);
}

void cta_sub_ticks(CtxHandler cHandle, const char* stdCode) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->stra_sub_ticks(stdCode);
}

bool cta_step(CtxHandler cHandle) {
    //只有异步模式才有意义
    if (!getRunner().isAsync())
        return false;

    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return false;

    return ctx->step_calc();
}

void cta_set_chart_kline(CtxHandler cHandle, const char* stdCode,
                         const char* period) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->set_chart_kline(stdCode, period);
}

void cta_add_chart_mark(CtxHandler cHandle, double price, const char* icon,
                        const char* tag) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->add_chart_mark(price, icon, tag);
}

void cta_register_index(CtxHandler cHandle, const char* idxName,
                        WtUInt32 indexType) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return;

    ctx->register_index(idxName, indexType);
}

bool cta_register_index_line(CtxHandler cHandle, const char* idxName,
                             const char* lineName, WtUInt32 lineType) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return false;

    return ctx->register_index_line(idxName, lineName, lineType);
}
bool cta_add_index_baseline(CtxHandler cHandle, const char* idxName,
                            const char* lineName, double val) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return false;

    return ctx->add_index_baseline(idxName, lineName, val);
}

bool cta_set_index_value(CtxHandler cHandle, const char* idxName,
                         const char* lineName, double val) {
    CtaMocker* ctx = getRunner().cta_mocker();
    if (ctx == NULL)
        return false;

    return ctx->set_index_value(idxName, lineName, val);
}

#pragma endregion "CTA策略接口"
